package edu.cicese.tm;

import com.jgoodies.looks.plastic.PlasticXPLookAndFeel;
import com.jsurf.InterestPoint;
import com.jsurf.Points;
import com.jsurf.SurfUtil;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Created by: Eduardo Quintana Contreras
 * Date: 24/02/11
 * Time: 11:05 AM
 */
public class FrameSpaceManagement extends JFrame {
	private Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
//	private static String appDir;

	private static FrameSpaceManagement frmSpaceManagement;
	private BufferedImage cachedMapImage;
	private JPanel mapPanel;
	private JList lstSpaces;
	private DefaultListModel lstSpacesModel;
	private JButton btnAddSpace, btnEditSpace, btnDeleteSpace, btnCancelSpace, btnMergeSpaces;
	private JButton btnChangePlan;
	private JLabel lblSpace, lblPlanInfo;

	private FrameSpace frmSpace;

	private List<Space> spaceList;
	public Map<String, Space> mpSpaces;
	private JSONArray jaSpaces;

	private final int MAP_WIDTH = 780, MAP_HEIGHT = 447;

	private BufferedImage planImage, originalPlanImage;
	public boolean selectingArea = false;
	public boolean newSpace = false;
	public Color colorSelected;
	public String spaceName;
	private int clickCount = 0;
	private List<Integer> xCoord = new ArrayList<Integer>(), yCoord = new ArrayList<Integer>();

	static {
		try {
			System.load("c:\\mingw\\bin\\LIBGCC_S_DW2-1.DLL");
			System.load("c:\\mingw\\bin\\LIBSTDC++-6.DLL");
			System.loadLibrary("opensurf");
		} catch (UnsatisfiedLinkError e) {
			e.printStackTrace();
		}
	}

	public FrameSpaceManagement() {

		Utilities.checkDirs();

//		PlasticLookAndFeel.setPlasticTheme(new DesertBlue());
		try {
			UIManager.setLookAndFeel(new PlasticXPLookAndFeel());
			UIManager.put("Panel.background", Color.WHITE);
			UIManager.put("OptionPane.background", Color.WHITE);
			UIManager.put("TextArea.inactiveBackground", Color.WHITE);
			UIManager.put("ProgressBar.selectionBackground", Color.DARK_GRAY);
			UIManager.put("ProgressBar.selectionForeground", Color.WHITE);
			UIManager.put("ToolTip.background", new Color(229, 244, 255));
			UIManager.put("ToolTip.foreground", Color.DARK_GRAY);
			LineBorder toolTipBorder = new LineBorder(new Color(173, 210, 255), 1);
			UIManager.put("ToolTip.border", toolTipBorder);
			Font tooltipFont = new Font("Segoe UI", Font.BOLD, 12);
			UIManager.put("ToolTip.font", tooltipFont);

			/*UIDefaults defaults = UIManager.getDefaults();
			Enumeration en = defaults.keys();
			while (en.hasMoreElements()) {
				Object key = en.nextElement();
				System.out.println(key + ": " + defaults.get(key));
			}*/
		} catch (UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		}
//		com.jgoodies.looks.plastic.theme.DesertBlue;
		loadSpaces();
	//	appDir = Utilities.path(new URI(getClass().getProtectionDomain().getCodeSource().getLocation().toString()).getPath()) + "\\spaces";
	//	appDir = Utilities.path(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

		frmSpaceManagement = this;
		setTitle("Administración de espacios");
		setSize(969, 485);
		setResizable(false);
		setLayout(null);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Rectangle window = getBounds();
		setLocation((screen.width - window.width) / 2, (screen.height - window.height) / 2);

		initComponents();
		updateButtonStates(Utilities.SINGLE_SELECTION);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		loadPlanImage();
		drawRooms();
//		Utilities.createChangeLog();
	}

	//! Creates the GUI components
	private void initComponents() {
		getContentPane().setBackground(Color.WHITE);
		setIconImage(new ImageIcon(getClass().getClassLoader().getResource("icons/icon.png")).getImage());

		mapPanel = new JPanel() {
			public void paintComponent(Graphics g) {
				super.paintComponent(g);
				g.drawImage(cachedMapImage, 0, 0, null);
			}
		};
		mapPanel.setBackground(Color.WHITE);
		mapPanel.setLayout(null);
		mapPanel.setBounds(5, 5, MAP_WIDTH, MAP_HEIGHT);
		mapPanel.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2 && !e.isConsumed()) {
					e.consume();
					openSpace(e.getX(), e.getY());
				}
			}
			public void mousePressed(MouseEvent e) {
				if (selectingArea) {
					int x = e.getX();
					int y = e.getY();
					if (!(e.isMetaDown() && clickCount < 2)) {
						xCoord.add(x);
						yCoord.add(y);
						clickCount++;
						if (e.isMetaDown() && clickCount > 2) {
							if (!DialogSpaceEdition.spaceEdited){
								addSpace(getNewSpaceID(), spaceName, colorSelected, xCoord, yCoord);
							}
							else {
								editSpace(getSelectedSpace().getSpaceID(), spaceName, colorSelected, xCoord, yCoord);
							}
							updateSpaceList();
							selectingArea = false;
							drawPolygon(x, y, true);
							planImage = cachedMapImage;
							updateButtonStates(Utilities.NO_SELECTION);
						}
					}
				}
				else {
//					openSpace(e.getX(), e.getY());
					selectSpace(e.getX(), e.getY());
				}
			}
		});
		mapPanel.addMouseMotionListener(new MouseMotionAdapter() {
			public void mouseMoved(MouseEvent e) {
				if (clickCount > 0) {
					drawPolygon(e.getX(), e.getY(), false);
				}
				if (!selectingArea) {
					updateSpaceName(e.getX(), e.getY());
				}
			}
		});

		lblPlanInfo = new JLabel("", SwingConstants.CENTER);
		SwingUtil.addLabel(lblPlanInfo, 0, 200, 780, 35,
				"Agregue un plano con el botón inferior derecho",
				null, mapPanel);
		lblPlanInfo.setHorizontalTextPosition(SwingConstants.CENTER);
		lblPlanInfo.setFont(new Font("Segoe UI", Font.BOLD, 25));
		lblPlanInfo.setForeground(Color.LIGHT_GRAY);
		lblPlanInfo.setVisible(false);

		JPanel buttonPanel = new JPanel();
		buttonPanel.setBounds(800, 5, 174, 447);
		buttonPanel.setLayout(null);

		// BUTTONS
		// Add
		btnAddSpace = new JButton();
		addButton(btnAddSpace, 5, 80, 32, 32, "Crear nuevo espacio", "addImg", buttonPanel, null);
		btnAddSpace.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionAddSpace();
			}
		});

		// Edit
		btnEditSpace = new JButton();
		addButton(btnEditSpace, 42, 80, 32, 32, "Editar espacio", "edit", buttonPanel, null);
		btnEditSpace.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionEditSpace();
			}
		});
		
		// Delete
		btnDeleteSpace = new JButton();
		addButton(btnDeleteSpace, 79, 80, 32, 32, "Eliminar espacio(s)", "delImg", buttonPanel, null);
		btnDeleteSpace.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionDeleteSpace();
			}
		});

		// Cancel
		btnCancelSpace = new JButton();
		addButton(btnCancelSpace, 5, 80, 32, 32, "Cancelar creación o edición de espacio", "cancel", buttonPanel, null);
		btnCancelSpace.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionCancelSpace();
			}
		});

		// Change Plan
		btnChangePlan = new JButton();
		addButton(btnChangePlan, 5, 360, 53, 45, "Cambiar plano", "plan", buttonPanel, null);
		btnChangePlan.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionChangePlan();
			}
		});

		// Merge
		btnMergeSpaces = new JButton("Unir");
		btnMergeSpaces.setSize(70, 25);
		btnMergeSpaces.setLocation(5, 355);
		btnMergeSpaces.setToolTipText("Unir dos o más espacios");
		btnMergeSpaces.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				actionMergeSpaces();
			}
		});


		// SPACE LIST
		JLabel lblSpaces = new JLabel("", SwingConstants.CENTER);
		SwingUtil.addLabel(lblSpaces, 5, 129, 150, 16, "Espacios", buttonPanel, null);
		lblSpaces.setFont(new Font("Segoe UI", Font.BOLD, 16));
		lstSpacesModel = new DefaultListModel();
		lstSpaces = new JList(lstSpacesModel);
		updateSpaceList();
		lstSpaces.setSize(150, 200);
		lstSpaces.setSelectedIndex(0);
		lstSpaces.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				listChanged(e.getValueIsAdjusting());
			}
		});
		lstSpaces.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				listClicked(e.getClickCount());
			}
		});

		JScrollPane pList = new JScrollPane(lstSpaces);
		pList.setBounds(5, 150, 150, 200);

		lblSpace = new JLabel();
		SwingUtil.addLabel(lblSpace, 5, 420, 150, 20, "", buttonPanel, null);

		buttonPanel.add(pList);

		getContentPane().add(mapPanel);
		getContentPane().add(buttonPanel);
	}

	//! Selects a space in the space list, according to the user selection.
	private void selectSpace(int x, int y) {
		Space space;
		for (Entry<String, Space> entry : mpSpaces.entrySet()) {
			space = entry.getValue();
			if (space.getSpaceArea().contains(x, y)) {
				for (int i = 0; i < spaceList.size(); i++) {
					if (spaceList.get(i).getSpaceID().equals(entry.getKey())) {
						lstSpaces.setSelectedIndex(i);
						break;
					}
				}
				break;
			}
		}
	}

	//! Gets the space selected in the map and opens it.
	private void openSpace(int x, int y) {
		Space space;
		for (Entry<String, Space> entry : mpSpaces.entrySet()) {
			space = entry.getValue();
			if (space.getSpaceArea().contains(x, y)) {
				openSpace(entry.getKey(), space.getSpaceName());
				break;
			}
		}
	}

	//! Opens a space.
	private void openSpace(String spaceID, String spaceName) {
		setVisible(false);
		if (frmSpace == null) {
			frmSpace = new FrameSpace(this);
		}
		frmSpace.setValues(spaceID, spaceName);
		frmSpace.setVisible(true);
		frmSpace.drawBackgrounds();
		Rectangle window = frmSpace.getBounds();
		frmSpace.setLocation((screen.width - window.width) / 2, (screen.height - window.height) / 2);
	}

	//! Displays the pointed spaceID name
	private void updateSpaceName(int x, int y) {
		for (Entry<String, Space> entry : mpSpaces.entrySet()) {
			if (entry.getValue().getSpaceArea().contains(x, y)) {
				lblSpace.setText(entry.getValue().getSpaceName());
				break;
			}
			else {
				lblSpace.setText("");
			}
		}
	}

	public void updateSpaceList() {
		lstSpacesModel.clear();
		spaceList = new ArrayList<Space>();
		for (Entry<String, Space> entry : mpSpaces.entrySet()) {
			spaceList.add(new Space(entry.getKey(), entry.getValue().getSpaceName()));
		}
		Collections.sort(spaceList);
		for (Space space : spaceList) {
			lstSpacesModel.addElement(space.getSpaceName());
		}
	}

	public Space getSelectedSpace() {
		return spaceList.get(lstSpaces.getSelectedIndex());
	}

	//! Called when the Add button is clicked.
	private void actionAddSpace() {
		if (mpSpaces.size() < 100) {
			newSpace = true;
			openSpaceEditionDialog();
		}
	}

	//! Called when the Edit button is clicked.
	private void actionEditSpace() {
		openSpaceEditionDialog();
	}

	//! Shows the space edition/creation dialog.
	private void openSpaceEditionDialog() {
		new DialogSpaceEdition(frmSpaceManagement);
		setEnabled(false);
	}

	//! Called when the Delete button is clicked. Deletes a space(s).
	private void actionDeleteSpace() {
		String s1 = "este", s2 = "espacio";
		if (lstSpaces.getSelectedIndices().length > 1) {
			s1 = "estos"; s2 = "espacios";
		}
		if (JOptionPane.showConfirmDialog(frmSpaceManagement,
				"¿Está seguro que desea eliminar " + s1 + " " + s2 + " de forma permanente?",
				"Eliminar espacio", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
				new ImageIcon(getClass().getClassLoader().getResource("icons/warning.png")))
				== JOptionPane.YES_OPTION) {
			List<Integer> itemsToRemove = new ArrayList<Integer>();
			for (int i : lstSpaces.getSelectedIndices()){
				deleteSpace(spaceList.get(i).getSpaceID());
				itemsToRemove.add(i);
			}

			int aux = 0;
			while (itemsToRemove.size() > 0) {
				lstSpacesModel.removeElementAt(itemsToRemove.get(0) - aux);
				itemsToRemove.remove(0);
				aux++;
			}
			updateSpaceList();
		}
	}

	//! Called when the Cancel button is clicked. Cancels a space creation/edition.
	private void actionCancelSpace() {
		if (selectingArea) {
			clickCount = 0;
			xCoord.clear();
			yCoord.clear();
			selectingArea = false;
			updateSpaceList();
			updateButtonStates(Utilities.NO_SELECTION);
		}
	}

	//! Called when the Change Plan button is clicked. Changes the plan image.
	private void actionChangePlan() {
		new FrameTest();

		/*JFileChooser fcPlanImg = new JFileChooser();
		PreviewPane previewPane = new PreviewPane();
		fcPlanImg.setAccessory(previewPane);
		fcPlanImg.addPropertyChangeListener(previewPane);
		FileNameExtensionFilter extensionFilter = new FileNameExtensionFilter("Imágenes .JPG y .PNG", "jpg", "png");
		fcPlanImg.setFileFilter(extensionFilter);
		fcPlanImg.setFileSelectionMode(JFileChooser.FILES_ONLY);
		if (fcPlanImg.showDialog(this, "Abrir") == JFileChooser.APPROVE_OPTION) {
			// change the plan image
			try {
				// make a copy of the image file
//				Utilities.copyFile(fcPlanImg.getSelectedFile(), new File (Utilities.appDir + "plan\\plan.jpg"));
				Utilities.copyFile(fcPlanImg.getSelectedFile().getCanonicalPath(), Utilities.appDir + "plan\\plan.jpg");
				loadPlanImage();
				drawRooms();
//				if (fcPlanImg.getSelectedFile().renameTo(new File (Utilities.appDir + "plan\\plan.jpg"))) {
//
//
//				}
//			    else {
//					JOptionPane.showMessageDialog(this, "No se pudo cargar la imagen", "Error al cargar la imagen",
//							JOptionPane.ERROR_MESSAGE,
//							new ImageIcon(getClass().getClassLoader().getResource("icons/error.png")));
//				}

			} catch (IOException e) {
				e.printStackTrace();
			}
		}*/
	}

	//! Called when the Merge button is clicked. Merges two or more spaces.
	private void actionMergeSpaces() {
		//TODO: Method.
	}

	//! Called when the list gets adjusted. Gets the number of spaces selected.
	private void listChanged(boolean adjusting) {
		if (!adjusting) {
			int selectionMode;
			switch (lstSpaces.getSelectedIndices().length) {
				case 1:
					selectionMode = Utilities.SINGLE_SELECTION;
					break;
				case 0:
					selectionMode = Utilities.NO_SELECTION;
					break;
				default:
					selectionMode = Utilities.MULTIPLE_SELECTION;
			}
			updateButtonStates(selectionMode);
		}
	}

	//! Called when the space list is clicked. Opens the space clicked.
	private void listClicked(int clicks) {
		if(clicks == 2) {
			if (lstSpaces.getSelectedIndices().length > 0) {
				openSpace(spaceList.get(lstSpaces.getSelectedIndex()).getSpaceID(),
						lstSpaces.getSelectedValue().toString());
			}
		}
	}

	//! Shows or hides the buttons accordingly.
	public void updateButtonStates(int option) {
		btnCancelSpace.setVisible(false);
		btnMergeSpaces.setVisible(false);
		switch (option) {
			case Utilities.SELECTING_AREA:
				btnCancelSpace.setVisible(true);
				btnAddSpace.setVisible(false);
				btnEditSpace.setVisible(false);
				btnDeleteSpace.setVisible(false);
				lstSpaces.setEnabled(false);
				break;
			case Utilities.SINGLE_SELECTION:
				btnAddSpace.setVisible(true);
				btnEditSpace.setVisible(true);
				btnDeleteSpace.setVisible(true);
				lstSpaces.setEnabled(true);
				break;
			case Utilities.NO_SELECTION:
				btnAddSpace.setVisible(true);
				btnEditSpace.setVisible(false);
				btnDeleteSpace.setVisible(false);
				lstSpaces.setEnabled(true);
				break;
			case Utilities.MULTIPLE_SELECTION:
				btnAddSpace.setVisible(true);
				btnEditSpace.setVisible(false);
				btnDeleteSpace.setVisible(true);
//				btnMergeSpaces.setVisible(true);
				lstSpaces.setEnabled(true);
				break;
		}
	}

	// Generates an spaceID ID
	@SuppressWarnings({"ConstantConditions"})
	private String getNewSpaceID() {
		int spaceID;
		File spaceDir = new File(Utilities.spaceDir);
		FileFilter filter = new FileFilter() {
      		public boolean accept(File file) {
          		return file.isDirectory();
      		}
		};
		File[] spaces = spaceDir.listFiles(filter);
		Map<Integer, String> spaceMap = new HashMap<Integer, String>();
		for (File space : spaces) {
			try {
				spaceMap.put(Integer.parseInt(space.getName()), "");
			} catch (Exception e) {
				System.out.println("Nombre de espacio incorrecto.");
			}
		}
		for (spaceID = 1; spaceID < 100 && spaceMap.containsKey(spaceID); spaceID++) { }
		return Utilities.addZeros(spaceID, 2);
	}

	//! Draws plan image
	private void loadPlanImage() {
		BufferedImage img;
		try {
			File planImg = new File(Utilities.appDir + "\\plan\\plan.jpg");
			if (planImg.exists()){
				img = ImageIO.read(planImg);
				BufferedImage tempImg = (BufferedImage)createImage(MAP_WIDTH, MAP_HEIGHT);
//				tempImg.flush();
				Graphics2D tempG = (Graphics2D)tempImg.getGraphics();
				tempG.drawImage(img.getScaledInstance(MAP_WIDTH, MAP_HEIGHT, Image.SCALE_AREA_AVERAGING), 0, 0, null);
				tempG.dispose();
				originalPlanImage = tempImg;
				planImage = tempImg;
				lblPlanInfo.setVisible(false);
			}
			else {
				lblPlanInfo.setVisible(true);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	//! Draws the current spaces
	private void drawRooms() {
		Graphics2D g2d = (Graphics2D)mapPanel.getGraphics();
		BufferedImage tempImg = (BufferedImage)createImage(MAP_WIDTH, MAP_HEIGHT);
		Graphics2D tempG = (Graphics2D)tempImg.getGraphics();

		try {
			if (originalPlanImage != null) {
				tempG.drawImage(originalPlanImage.getScaledInstance(
						MAP_WIDTH, MAP_HEIGHT, Image.SCALE_FAST), 0, 0, null);
			}
			else {
				tempG.setColor(Color.WHITE);
				tempG.fillRect(0, 0, MAP_WIDTH, MAP_HEIGHT);
			}
			tempG.setStroke(new BasicStroke(3f));

			for (Entry<String, Space> entry : mpSpaces.entrySet()) {
				Color color = entry.getValue().getSpaceColor();
				tempG.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 100));
				tempG.drawPolygon(entry.getValue().getSpaceArea());
				tempG.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 60));
				tempG.fillPolygon(entry.getValue().getSpaceArea());
			}

			tempG.dispose();
			g2d.drawImage(tempImg, null, 0, 0);
			cachedMapImage = tempImg;
			planImage = cachedMapImage;
		} catch (Exception e) { e.printStackTrace(); }
	}

	//! Draws the selection in the plan image
	private void drawPolygon(int x, int y, boolean save) {
		int size = xCoord.size();
		int[] xCoordArray, yCoordArray;
		if (!save) {
			xCoordArray = new int[size + 1];
			yCoordArray = new int[size + 1];
			xCoordArray[size] = x;
			yCoordArray[size] = y;
		}
		else {
			xCoordArray = new int[size];
			yCoordArray = new int[size];
		}

		for (int i = 0; i < size; i++) {
			xCoordArray[i] = xCoord.get(i);
			yCoordArray[i] = yCoord.get(i);
		}

		Polygon polygon = new Polygon(xCoordArray, yCoordArray, xCoordArray.length);

		Graphics2D g2d = (Graphics2D)mapPanel.getGraphics();
		BufferedImage tempImg = (BufferedImage)createImage(MAP_WIDTH, MAP_HEIGHT);
		Graphics2D tempG = (Graphics2D)tempImg.getGraphics();
		tempG.drawImage(planImage, 0, 0, null);
		tempG.setStroke(new BasicStroke(3f));
		tempG.setColor(new Color(colorSelected.getRed(), colorSelected.getGreen(), colorSelected.getBlue(), 100));
		tempG.drawPolygon(polygon);
		tempG.setColor(new Color(colorSelected.getRed(), colorSelected.getGreen(), colorSelected.getBlue(), 60));
		tempG.fillPolygon(polygon);
		tempG.dispose();

		g2d.drawImage(tempImg, null, 0, 0);
		cachedMapImage = tempImg;

		if (save) {
			clickCount = 0;
			xCoord.clear();
			yCoord.clear();
		}
	}

	//! Creates a new spaceID
	@SuppressWarnings({"unchecked", "ResultOfMethodCallIgnored"})
	private void addSpace(String spaceID, String spaceName, Color spaceColor, List<Integer> xCoord, List<Integer> yCoord) {
		JSONObject joSpace = new JSONObject();
		joSpace.put("spaceID", spaceID);
		joSpace.put("spaceName", spaceName);
		joSpace.put("spaceColor", String.valueOf(spaceColor.getRGB()));
		joSpace.put("xCoord", JSONValue.parse(JSONArray.toJSONString(xCoord)));
		joSpace.put("yCoord", JSONValue.parse(JSONArray.toJSONString(yCoord)));
		jaSpaces.add(joSpace);
		Utilities.saveString(Utilities.appDir + "\\spaces\\spaces.ans", jaSpaces.toJSONString());
//		saveSpacesToFile(jaSpaces.toJSONString());
		mpSpaces.put(spaceID, new Space(spaceName, spaceColor, xCoord, yCoord));
//		File newSpace = new File(Utilities.getTagDir(spaceID));
		new File(Utilities.getTagDir(spaceID)).mkdirs();
//		newSpace = new File(Utilities.getImgDir(spaceID));
//		newSpace.mkdirs();
		createDirectories(spaceID);
		addSpaceChangelog(spaceID);
		drawRooms();
	}

	//! Creates a spaceID resolution directories
	@SuppressWarnings({"ResultOfMethodCallIgnored"})
	private void createDirectories(String spaceID) {
		for (String[] r : Utilities.imgResolutions) {
			new File(Utilities.getImgDir(spaceID) + r[0]).mkdirs();
		}


		//TODO: Check if the loop below is needed, if not, leave the next lines.
		new File(Utilities.getPoiDir(spaceID, "480x360", "001")).mkdirs();
		new File(Utilities.getPoiDir(spaceID, "800x600", "0001")).mkdirs();

//		/*for (String r : Utilities.poiResolutions) {
//			for (String t : Utilities.thresholds) {
//				new File(Utilities.getPoiDir(spaceID, r, t)).mkdirs();
//			}
//		}*/
	}

	//! Changes a spaceID name and/or color
	@SuppressWarnings("unchecked")
	public void editSpace(String spaceID, String spaceName, Color spaceColor, List<Integer> xCoord, List<Integer> yCoord) {
		Space space = mpSpaces.get(spaceID);
		space.setSpaceName(spaceName);
		space.setSpaceColor(spaceColor);
		if (xCoord != null){
			space.setSpaceArea(xCoord, yCoord);
		}
		// Save changes
		JSONObject joSpace;
		for (Object obj : jaSpaces) {
			joSpace = (JSONObject) obj;
			if (joSpace.get("spaceID").toString().equals(spaceID)) {
				joSpace.put("spaceName", spaceName);
				joSpace.put("spaceColor", String.valueOf(spaceColor.getRGB()));
				if (xCoord != null) {
					joSpace.put("xCoord", JSONValue.parse(JSONArray.toJSONString(xCoord)));
					joSpace.put("yCoord", JSONValue.parse(JSONArray.toJSONString(yCoord)));
				}
				break;
			}
		}
//		saveSpacesToFile(jaSpaces.toJSONString());
		Utilities.saveString(Utilities.appDir + "\\spaces\\spaces.ans", jaSpaces.toJSONString());
		drawRooms();
	}

	//! Deletes a spaceID
	private void deleteSpace(String spaceID) {
		mpSpaces.remove(spaceID);
		// Save changes
		JSONObject joSpace = new JSONObject();
		boolean spaceFound = false;
		for (Object obj : jaSpaces) {
			joSpace = (JSONObject) obj;
			if (joSpace.get("spaceID").toString().equals(spaceID)) {
				spaceFound = true;
				break;
			}
		}
		if (spaceFound) { // just in case, but it should always find the spaceID.
			jaSpaces.remove(joSpace);
		}
		deleteSpaceChangelog(spaceID);
		Utilities.deleteDirectory(Utilities.spaceDir + spaceID);
//		saveSpacesToFile(jaSpaces.toJSONString());
		Utilities.saveString(Utilities.appDir + "\\spaces\\spaces.ans", jaSpaces.toJSONString());
		drawRooms();
	}

	//! Loads the list of spaces
	private void loadSpaces() {
		try {
			File spacesFile = new File(Utilities.appDir + "\\spaces\\spaces.ans");
			mpSpaces = new HashMap<String, Space>();
			if (spacesFile.exists()) {
				BufferedReader br = new BufferedReader(new FileReader(spacesFile));
				jaSpaces = (JSONArray) JSONValue.parse(br.readLine());
				JSONArray jaxCoord, jayCoord;
				JSONObject joSpace;
				String spaceID, spaceName, spaceColor;
				List<Integer> xCoord, yCoord;

				for (Object obj : jaSpaces) {
					joSpace = (JSONObject) obj;
					spaceID = joSpace.get("spaceID").toString();
					spaceName = joSpace.get("spaceName").toString();
					spaceColor = joSpace.get("spaceColor").toString();
					jaxCoord = (JSONArray) joSpace.get("xCoord");
					jayCoord = (JSONArray) joSpace.get("yCoord");
					xCoord = new ArrayList<Integer>();
					yCoord = new ArrayList<Integer>();
					for (int c = 0; c < jaxCoord.size(); c++) {
						xCoord.add(Integer.valueOf(jaxCoord.get(c).toString()));
						yCoord.add(Integer.valueOf(jayCoord.get(c).toString()));
					}
					mpSpaces.put(spaceID, new Space(spaceName, new Color(Integer.parseInt(spaceColor)), xCoord, yCoord));
				}
			} else {
				jaSpaces = new JSONArray();
				Utilities.saveString(Utilities.appDir + "\\spaces\\spaces.ans", jaSpaces.toJSONString());
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
		// Load file spaces
		// + "\\spaces"

		// TODO: Check missing directories??
	}

	//! Adds a 'spaceID creation' entry to the changelog
	@SuppressWarnings({"unchecked"})
	private void addSpaceChangelog(String spaceID) {
//		JSONArray jaChangelog = (JSONArray) JSONValue.parse(Utilities.loadString(Utilities.spaceDir + "changelog.ans"));
		JSONArray jaChangelog = Utilities.loadJSONArray(Utilities.spaceDir + "changelog.ans");
		JSONObject joSpace = new JSONObject();
		joSpace.put("spaceID", spaceID);
		joSpace.put("action", Utilities.CREATED);
		joSpace.put("tags", new JSONArray());
		jaChangelog.add(joSpace);
		Utilities.saveString(Utilities.spaceDir + "changelog.ans", jaChangelog.toJSONString());
	}

	//! Adds a 'spaceID deletion' entry to the changelog
	@SuppressWarnings({"unchecked"})
	private void deleteSpaceChangelog(String spaceID) {
		JSONArray jaChangelog = Utilities.loadJSONArray(Utilities.spaceDir + "changelog.ans");
		JSONObject joSpace = new JSONObject();
		boolean spaceFound = false;
		for (Object obj : jaChangelog) {
			joSpace = (JSONObject) obj;
			if (joSpace.get("spaceID").toString().equals(spaceID) &&
					Integer.parseInt(joSpace.get("action").toString()) != Utilities.DELETED) {
				spaceFound = true;
				break;
			}
		}
		if (!spaceFound) {
			joSpace = new JSONObject();
			joSpace.put("spaceID", spaceID);
			joSpace.put("action", Utilities.DELETED);
			joSpace.put("tags", new JSONArray());
			jaChangelog.add(joSpace);
		}
		else {
			if (Integer.parseInt(joSpace.get("action").toString()) == Utilities.NO_ACTION) {
				joSpace.put("action", Utilities.DELETED);
				joSpace.put("tags", new JSONArray());
			}
			else if (Integer.parseInt(joSpace.get("action").toString()) == Utilities.CREATED) {
				jaChangelog.remove(joSpace);
			}
		}
		// save to the changelog file used to update the server poi repository cache
		Utilities.addToLog(spaceID);
		// save to the changelog file used to update the client tag repository
		Utilities.saveString(Utilities.spaceDir + "changelog.ans", jaChangelog.toJSONString());
	}

	//! Adds a JButton
	private void addButton(JButton button, int x, int y, int width, int height,
	                              String toolTipText, String iconName, JPanel pane, Container container) {
		Icon icon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + ".png"));
		Icon rIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "O.png"));
		Icon pIcon = new ImageIcon(getClass().getClassLoader().getResource("icons/" + iconName + "P.png"));
		SwingUtil.addButton(button, x, y, width, height, toolTipText, icon, rIcon, pIcon, pane, container);
	}

	//! EXPERIMENT: Compares one image against the stored POIs
	@SuppressWarnings({"ConstantConditions"})
	private static void compare(String queryFile, String imgName, File logFile) {
		long begin, end;
		try {
    		List<Match> matches = new ArrayList<Match>();
			Points query = SurfUtil.loadPointsFromFile(queryFile);

			begin = System.currentTimeMillis();
			// for each stored POI
			for (Entry<String, Points> entry : SurfUtil.storedPOIs.entrySet()) {
				Map<InterestPoint, InterestPoint> matchedPoints = SurfUtil.getMatches(query.points, entry.getValue().points);
				matches.add(new Match(matchedPoints.size(), entry.getKey(), entry.getValue().spaceID));
			}
			Collections.sort(matches);
			end = System.currentTimeMillis();

			String log = imgName + " " + query.spaceID;
			for (int x = 0; x < 5 && x < matches.size(); x++) {
				log += " " + matches.get(x).filename + " " + matches.get(x).space + " " + matches.get(x).quantity + " " + (double)(matches.get(x).quantity) / query.points.size();
			}
			log += " " + ((double)(end - begin) / 1000) + "\n";
			BufferedWriter out = new BufferedWriter(new FileWriter(logFile, true));
			out.write(log);
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	//! EXPERIMENT: Compares with labels
	@SuppressWarnings({"ConstantConditions"})
	private static void compareTags(String queryFile, String imgName, File logFile, boolean spaceKnown) {
		long begin, end;
		double total;

		try {
    		Points query = SurfUtil.loadPointsFromFile(queryFile);

			BufferedWriter out = new BufferedWriter(new FileWriter(logFile, true));
			String log = "";
			List<Match> matches = new ArrayList<Match>();
			begin = System.currentTimeMillis();

			// for each stored label
			for (Entry<String, Points> entry : SurfUtil.storedTags.entrySet()) {
				Map<InterestPoint, InterestPoint> matchedPoints = SurfUtil.getMatches(query.points, entry.getValue().points);
				matches.add(new Match(matchedPoints.size(), entry.getKey()));
			}

			Collections.sort(matches);

			end = System.currentTimeMillis();
			total = end - begin;

			log += imgName + " " + query.points.size();

			String aux;
			for (int x = 0; x < 5 && x < matches.size(); x++) {
				if (spaceKnown) {
					aux = "01 " + matches.get(x).filename;
				}
				else {
					aux = matches.get(x).filename.substring(0, 2) + " " + matches.get(x).filename.substring(2);
				}
				log += " " + aux + " " + matches.get(x).quantity + " " +
						(double)(matches.get(x).quantity) / query.points.size();
			}

			log += " " + (total / 1000) + "\n";
			out.write(log);
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] a) {
		new FrameSpaceManagement();
//		FrameSpaceManagement.frmSpaceManagement = main;

//		SurfUtil.poiDir = "D:\\Tests\\Experimento999\\queries\\640x480\\pois";
//		SurfUtil.poiDir = Utilities.poiDir;
//
//		String queryDir = "D:\\Tests\\Experimento999\\queries\\640x480\\pois\\";
//		String logDir = "D:\\Tests\\Experimento999\\logs\\";
//		File logFile = new File(logDir + "exp1.1.log");

		//Experiment 1.1
//		SurfUtil.loadPOIs();
//		for (int q = 1; q < 11; q++) {
//			compare(queryDir + Utilities.addZeros(q, 2) + ".poi", Utilities.addZeros(q, 2), logFile);
//		}

		//Experiment 1.2
//		SurfUtil.loadAllTags();
//		logFile = new File(logDir + "exp1.2.log");
//		for (int q = 1; q < 11; q++) {
//			compareTags(queryDir + Utilities.addZeros(q, 2) + ".poi", Utilities.addZeros(q, 2), logFile, false);
//		}

		//Experiment 1.3
//		queryDir = "D:\\Tests\\Experimento999\\queries2\\640x480\\pois\\";
//		SurfUtil.loadAllTags();
//		logFile = new File(logDir + "exp1.3.log");
//		for (int q = 1; q < 11; q++) {
//			compareTags(queryDir + Utilities.addZeros(q, 2) + ".poi", Utilities.addZeros(q, 2), logFile, false);
//		}

		//Experiment 1.4
//		queryDir = "D:\\Tests\\Experimento999\\queries2\\640x480\\pois\\";
//		SurfUtil.loadTags("01");
//		logFile = new File(logDir + "exp1.4.log");
//		for (int q = 1; q < 11; q++) {
//			compareTags(queryDir + Utilities.addZeros(q, 2) + ".poi", Utilities.addZeros(q, 2), logFile, true);
//		}
	}

	static class PreviewPane extends JPanel implements PropertyChangeListener {
		private JLabel lblImgPreview;
		private int maxImgWidth;
		public PreviewPane() {
			setLayout(new BorderLayout(5, 5));
			setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
			add(new JLabel("Vista previa:"), BorderLayout.NORTH);
			lblImgPreview = new JLabel();
			lblImgPreview.setBackground(Color.WHITE);
			lblImgPreview.setOpaque(true);
			lblImgPreview.setPreferredSize(new Dimension(400, 400));
			maxImgWidth = 395;
			lblImgPreview.setBorder(BorderFactory.createEtchedBorder());
			add(lblImgPreview, BorderLayout.CENTER);
		}

		public void propertyChange(PropertyChangeEvent evt) {
			Icon icon = null;
			if(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(evt.getPropertyName())) {
				File newFile = (File)evt.getNewValue();
				if(newFile != null) {
					String path = newFile.getAbsolutePath();
					if(path.toLowerCase().endsWith(".jpg") || path.endsWith(".png")) {
						try {
							BufferedImage img = ImageIO.read(newFile);
							float width = img.getWidth();
							float height = img.getHeight();
							float scale = height / width;
							width = maxImgWidth;
							height = (width * scale); // height should be scaled from new width
							icon = new ImageIcon(img.getScaledInstance(
									Math.max(1, (int)width), Math.max(1, (int)height), Image.SCALE_FAST));
						}
						catch(IOException e) {
							// couldn't read image.
						}
					}
				}

				lblImgPreview.setIcon(icon);
				this.repaint();
			}
		}
	}
}
